Skip to content

Conversation

@calebzulawski
Copy link
Member

@calebzulawski calebzulawski commented Nov 27, 2025

This improves type inference by removing most associated types. For example, the following function doesn't actually work:

fn foo<T: SimdElement>(x: Simd<T, 4>) -> Mask<T::Mask, 4>
where
    Simd<T, 4>: SimdFloat,
{
    x.is_sign_negative()
}

You actually need the constraint Simd<T, 4>: SimdFloat<Mask = Mask<T::Mask, 4>>.

With this PR, the function should look a little more like:

fn foo<T: SimdElement>(x: Simd<T, 4>) -> Mask<T::Mask, 4>
where
    Simd<T, 4>: SimdFloat<T, 4>,
{
    x.is_sign_negative()
}

There are still a few instances that need associated types, but we can explore those individually to see if there's a better API.

Also, note that we now end up with a lot of instances of Mask<<T as SimdElement>::Mask, N>. We can either remove the SimdElement::Mask associated type with #483 or we can provide an alias to avoid writing that out.

/// The mask type returned by each comparison.
type Mask;

pub trait SimdPartialEq<T, const N: usize>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo N should be an associated #[type_const] but last I knew that's not done being implemented yet.

something like:

pub trait SimdLen {
    #[type_const]
    const LEN: usize;
}

impl<T: SimdElement, const N: usize> SimdLen for Simd<T, N> {
    #[type_const]
    const LEN: usize = N;
}

impl<T: MaskElement, const N: usize> SimdLen for Mask<T, N> {
    #[type_const]
    const LEN: usize = N;
}

pub trait SimdBase: SimdLen {
    type Element: SimdElement;
}

impl<T: SimdElement, const N: usize> SimdBase for Simd<T, N> {
    type Element = T;
}

pub trait SimdPartialEq: SimdBase {
    fn simd_eq(self, other: Self) -> Mask<<Self::Element as SimdElement>::Mask, { Self::LEN }>;
    ...
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that would allow your example function's bounds to just be:

fn foo<T: SimdElement>(x: Simd<T, 4>) -> Mask<T::Mask, 4>
where
    // output types are completely deduced from the blanket
    // SimdBase and SimdLen impls so no additional bounds are needed
    Simd<T, 4>: SimdFloat,
{
    x.is_sign_negative()
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sadly, it seems that hits the bug where specifying bounds hides information that could be deduced so the bound ends up having to be:
Simd<T, 4>: SimdFloat<Element = T, LEN = 4>

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=c84993a34513e1f45a01b986533be269

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got it to work with just Simd<T, 4>: SimdFloat by removing SimdFloat: SimdBase and instead having where Self: SimdBase on all items in the trait.

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=c13634f70837e4b761b6a19592d069d9

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants